#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define HOLE_SIZE_MIN 16

static int32_t find_hole(const char *buf, size_t nread, size_t *hole_start, size_t *hole_size) {
    size_t i;
    for (i = 0; i < nread; i++) {
        if (buf[i] == 0) {
            *hole_start = i;
            while (i < nread && buf[i] == 0) {
                i++;
            }
            *hole_size = i - *hole_start;
            if (*hole_size < HOLE_SIZE_MIN) {
                *hole_start = 0;
                *hole_size = 0;
                continue;
            }
            return 1;
        }
    }
    return 0;
}

int main(int argc, char *argv[]) {
    int ret = 0;
    int fd_src, fd_dst;
    char buf[BUFSIZ];
    ssize_t nread, nwrite;
    size_t hole_start = 0;
    size_t hole_size = 0;
    char *p = NULL;

    if (argc != 3) {
        fprintf(stderr, "Usage: %s <source> <destination>\n", argv[0]);
        exit(1);
    }
    
    fd_src = open(argv[1], O_RDONLY);
    if (fd_src == -1) {
        perror("open");
        exit(1);
    }
    
    fd_dst = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd_dst == -1) {
        perror("open");
        ret = 1;
        goto src_err;
    }
    
    while ((nread = read(fd_src, buf, sizeof(buf))) > 0) {
        hole_start = 0;
        hole_size = 0;
        p = buf;
        while (find_hole(p, nread, &hole_start, &hole_size)) {
            printf("find a hole: hole_start: %zu, hole_size: %zu\n", hole_start, hole_size);
            if (hole_start > 0) {
                nwrite = write(fd_dst, p, hole_start);
                if (nwrite == -1) {
                    perror("write");
                    ret = 1;
                    goto quit;
                }
            }
            lseek(fd_dst, hole_size, SEEK_CUR);
            p += hole_start + hole_size;
            nread -= hole_start + hole_size;
        }

        nwrite = write(fd_dst, p, nread);
        if (nwrite == -1) {
            perror("write");
            ret = 1;
            goto quit;
        }
        if (nwrite != nread) {
            fprintf(stderr, "write error\n");
            ret = 1;
            goto quit;
        }
    }

    if (nread == -1) {
        perror("read");
        ret = 1;
    }

quit:
    close(fd_dst);
src_err:
    close(fd_src);
    return ret;
}
